﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using WoWTimer.Classes;
using SpeechLib;
using WoWTimer.Properties;

namespace WoWTimer
{
    public partial class MainForm : Form
    {
        private DateTime _finish;

        const string APP_NAME = "WoWTimer";
        const int MULTIPLIER = 60000; // Minutes
        string[] FILENAMES = { "Broker_Wintergrasper.lua", "Broker_TolBarad.lua" };
        string[] BATTLES = { "Wintergrasp", "Tol Barad" };

        const string WOW_TIMER_WEBSITE = @"https://code.google.com/p/wow-timer/";

        const char FACTION_UNKNOWN = '0';
        const char FACTION_ALLIANCE = '1';
        const char FACTION_HORDE = '2';
        const char FACTION_BATTLE = '3';

        public static string ConfigFile;

        readonly List<Battle> _battles;
        readonly Options _options;
        Battle _selectedBattle;

        private enum LuaCorrectness { Ok, FileDoesNotExist, WrongFileStructure, EmptyFileName };

        public MainForm()
        {
            InitializeComponent();

            ConfigFile = Application.StartupPath + @"\config.xml";

            // Load options
            _options = OptionsReadWrite.Load(ConfigFile);

            // Read data from WTF
            _battles = new List<Battle>();
            BrowseForSavedVariables();
            ReadBattlesFromSavedVariables();
            _battles.Sort();

            // Populate ComboBox
            foreach (Battle b in _battles)
            {
                if (b.Time > DateTime.Now)
                    comboBoxNextBattles.Items.Add(b);
            }

            // If at least one entry is found, select the first entry in the ComboBox
            if (comboBoxNextBattles.Items.Count > 0)
                comboBoxNextBattles.SelectedIndex = 0;
            else
            {
                buttonStartTimer.Enabled = false;
                numericUpDownAlarmTime.Enabled = false;
                comboBoxNextBattles.Enabled = false;
            }

            // Show try icon
            notifyIcon1.Visible = true;
        }

        private void Timer1Tick(object sender, EventArgs e)
        {
            // Stop both timers
            timer1.Stop();
            timer2.Stop();

            // Set label2 to finished
            label2.Text = "Finished!";

            // Speak the text
            string speachText = _options.SpeachText;

            if (speachText.Contains("$ZONE$"))
                speachText = speachText.Replace("$ZONE$", _selectedBattle.Type);

            if (speachText.Contains("$REALM$"))
                speachText = speachText.Replace("$REALM$", _selectedBattle.Realm);

            if (speachText.Contains("$REMAINING$"))
                speachText = speachText.Replace("$REMAINING$", _options.Remaining.ToString());
            try
            {
                const SpeechVoiceSpeakFlags spFlags = SpeechVoiceSpeakFlags.SVSFlagsAsync;
                SpVoice voice = new SpVoice { Volume = _options.Volume };
                voice.Speak(speachText, spFlags);
            }
            catch (Exception)
            {
                MessageBox.Show("Speak error", APP_NAME, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            // Show BalloonTip
            notifyIcon1.BalloonTipTitle = "Timer for " + _selectedBattle.Realm + " has fired.";
            notifyIcon1.BalloonTipText = speachText;
            notifyIcon1.ShowBalloonTip(10000);

            // Show MessageBox
            MessageBox.Show(speachText, APP_NAME, MessageBoxButtons.OK, MessageBoxIcon.Information);

            // Shutdown Application
            Application.Exit();
        }

        private void Timer2Tick(object sender, EventArgs e)
        {
            string remaining = RemainingTime(_finish);
            SetRemainingTime(remaining);
        }

        private void ButtonStartTimer_Click(object sender, EventArgs e)
        {
            DateTime battleStart = ((Battle)comboBoxNextBattles.SelectedItem).Time;
            TimeSpan alarm = new TimeSpan(0, (int)numericUpDownAlarmTime.Value, 0);
            TimeSpan remaining = new TimeSpan(battleStart.Ticks - DateTime.Now.Ticks - alarm.Ticks);
            if (remaining.Ticks < 0)
            {
                MessageBox.Show("Although science has made major improvements over the last decade,\n time travel is still NOT possible!");
                return;
            }

            timer1.Interval = (int)(remaining.Ticks / 10000);
            _selectedBattle = (Battle)comboBoxNextBattles.SelectedItem;

            if (((Battle)comboBoxNextBattles.SelectedItem).Faction.Equals("Alliance"))
                notifyIcon1.Icon = Resources.alliance;
            else if (((Battle)comboBoxNextBattles.SelectedItem).Faction.Equals("Horde"))
                notifyIcon1.Icon = Resources.horde;
            else
                notifyIcon1.Icon = Resources.wow;

            timer1.Start();
            timer2.Start();

            _finish = battleStart.Subtract(new TimeSpan(0, (int)numericUpDownAlarmTime.Value, 0));
            SetRemainingTime(RemainingTime(_finish));

            // Disable the form's controls
            buttonStartTimer.Enabled = false;
            numericUpDownAlarmTime.Enabled = false;
            comboBoxNextBattles.Enabled = false;
        }

        private void NumericUpDown1ValueChanged(object sender, EventArgs e)
        {
            timer1.Interval = (int)numericUpDownAlarmTime.Value * MULTIPLIER;
        }

        private void MainWindowResize(object sender, EventArgs e)
        {
            if (FormWindowState.Minimized != WindowState) return;
            Hide();
        }

        private void NotifyIcon1_DoubleClick(object sender, MouseEventArgs e)
        {
            Show();
            WindowState = FormWindowState.Normal;
        }

        private void RestoreToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Show();
            WindowState = FormWindowState.Normal;
        }

        private void quitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }


        #region Private Helpers

        private static String RemainingTime(DateTime time)
        {
            TimeSpan diff = time - DateTime.Now;
            DateTime test = new DateTime(diff.Ticks);

            return test.ToString("HH:mm:ss");
        }

        // Sets the label in the main window and the tooltip in traybar to remaining time
        private void SetRemainingTime(string remaining)
        {
            label2.Text = remaining;
            notifyIcon1.Text = remaining + " - " + _selectedBattle.Realm;
        }

        private void ReadBattlesFromSavedVariables()
        {
            for (int i = 0; i < FILENAMES.Length; ++i)
            {
                // Create reader and open file
                String path = _options.LuaDir + FILENAMES[i];
                using (TextReader tr = new StreamReader(path))
                {
                    string buffer;
                    while ((buffer = tr.ReadLine()) != null)
                    {
                        if (buffer.Contains("[\"realm\"]"))
                            break;
                    }
                    while ((buffer = tr.ReadLine()) != null)
                    {
                        int startIndex, endIndex, length;
                        string realm, faction = "", label;
                        long seconds = 0;
                        bool[] found = {false, false};

                        // Test if more realms
                        if (buffer.Contains("},"))
                            break;

                        // Read and parse realm name
                        startIndex = buffer.IndexOf("[\"") + 2;
                        endIndex = buffer.IndexOf("\"]");
                        length = endIndex - startIndex;
                        realm = buffer.Substring(startIndex, length);

                        // Read and parse time + faction
                        while ((buffer = tr.ReadLine()) != null)
                        {
                            if (buffer.Contains("},"))
                            {
                                if (found[0] && found[1])
                                {
                                    // Create a battle object and add it to the battles list
                                    Battle battle = new Battle(BATTLES[i], realm, faction, seconds);
                                    _battles.Add(battle);
                                }
                                break;
                            }
                            startIndex = buffer.IndexOf("[\"") + 2;
                            endIndex = buffer.IndexOf("\"]");
                            length = endIndex - startIndex;
                            label = buffer.Substring(startIndex, length);
                            if (label.Equals("nextBattleTime"))
                            {
                                startIndex = buffer.IndexOf("=") + 2;
                                endIndex = buffer.IndexOf(",");
                                length = endIndex - startIndex;
                                buffer = buffer.Substring(startIndex, length);
                                seconds = long.Parse(buffer);
                                found[0] = true;
                            }
                            if (label.Equals("nextBattleFaction"))
                            {
                                startIndex = buffer.IndexOf("=") + 2;
                                length = 1;
                                char fact = (buffer.Substring(startIndex, 1))[0];
                                if (fact == FACTION_ALLIANCE)
                                    faction = "Alliance";
                                else if (fact == FACTION_HORDE)
                                    faction = "Horde";
                                else
                                    faction = "Unknown";
                                found[1] = true;
                            }
                       }
                    }
                    // Close the stream
                    tr.Close();
                }
            }
        }

        private void BrowseForSavedVariables()
        {
            LuaCorrectness correct;

            while ((correct = IsLuaFileCorrect(_options.LuaFile)) != LuaCorrectness.Ok)
            {
                if (correct == LuaCorrectness.EmptyFileName)
                    MessageBox.Show(
                        "This seems to be the first time you start WoWTimer.\nPlease browse to \"<wow install directory>\\WTF\\Account\\<your account>\\SavedVariables\\\" and select \"" +
                        FILENAMES[0] + "\" or \"" + FILENAMES[1] + "\".", "Welcome to WoWTimer", MessageBoxButtons.OK, MessageBoxIcon.Information);
                if (correct == LuaCorrectness.FileDoesNotExist)
                    MessageBox.Show("The file \"" + _options.LuaFile + "\" does not exist.", "File not found",
                                    MessageBoxButtons.OK, MessageBoxIcon.Error);

                if (correct == LuaCorrectness.WrongFileStructure)
                    MessageBox.Show(
                        "You provided a wrong file.\nPlease browse to <wow install directory>\\WTF\\Account\\<your account>\\SavedVariables\\ and select \"" +
                        FILENAMES[0] + "\" or \"" + FILENAMES[1] + "\".", "Wrong file", MessageBoxButtons.OK, MessageBoxIcon.Error);

                OpenFileDialog fileDialog = new OpenFileDialog { Multiselect = false };
                DialogResult result = fileDialog.ShowDialog();
                
                // If a user presses cancel, exit the application
                if (result == DialogResult.Cancel)
                    Environment.Exit(0);

                _options.LuaFile = fileDialog.FileName;
            }

            // Get dir from file path
            String buffer = _options.LuaFile;
            int endIndex = buffer.IndexOf("\\Broker_");
            _options.LuaDir = buffer.Substring(0, endIndex + 1);

            // Save the options
            OptionsReadWrite.Save(ConfigFile, _options);
        }

        private static LuaCorrectness IsLuaFileCorrect(string filename)
        {
            if (filename == "") return LuaCorrectness.EmptyFileName;
            if (!new FileInfo(filename).Exists) return LuaCorrectness.FileDoesNotExist;

            using (TextReader tr = new StreamReader(filename))
            {
                string buffer;
                while ((buffer = tr.ReadLine()) != null)
                {
                    if (buffer.Contains("Broker_WintergrasperDB")) return LuaCorrectness.Ok;
                    if (buffer.Contains("Broker_TolBaradDB")) return LuaCorrectness.Ok;
                }
            }

            return LuaCorrectness.WrongFileStructure;
        }

        #endregion // Private Helpers


        private void ExitToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void OptionsToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            OptionsForm optionsForm = new OptionsForm(_options);
            optionsForm.ShowDialog();

            // set the numericUpDown control to the options value
            numericUpDownAlarmTime.Value = _options.Remaining;
        }

        private void HelpToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            Process.Start(WOW_TIMER_WEBSITE);
        }

        private void AboutToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            AboutForm about = new AboutForm();
            about.ShowDialog();
        }
    }
}
